
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;



/**
 * A class representing a Service object within the IBMS system.
 * 
 * A Service is an instance of a Bus Journey on a
 * particular route. It consists of a Route, and a list of
 * times that a Bus arrives at particular destinations.
 * 
 * @version 1.1
 */
public class Service
{

    private static final int MINUTES_IN_DAY = 1440;

    //The kind of day this service is
    private TimetableInfo.timetableKind kind;
    //The ID of this particular service
    private int serviceID;
    //The Number of the particular service. The number is the index
    //which you use to access a service ID from TimetableInfo.getServices()
    private int serviceNumber;
    //The route which this particular service operates on
    private Route route;
    //An array of times a Service visits all the locations within a route
    private ArrayList<Calendar> times;
    //The day the service should be on
    private Calendar serviceDay;
    //The duration of the shift in minutes
    private int durationInMinutes;

    // following attributes added by Yuri
    // The bus stops, that are visited by this Service
    private ArrayList<Location> busStops;
    // type of the route: Circular - start stop corresponds with end stop
    //                    Straight - otherwise
    public static enum Type {CIRCULAR, STRAIGHT, NONE};
    private Type type;

    //Whether the Service is canceller
    private boolean isCancelled;
    //The message about cancellation
    private String message;
    //The delay amount in minutes
    private int delayAmount;

    /**
     * Creates a Service object without a Bus attached to it, and generates
     * the times that a bus should arrive at particular locations on its Route.
     * @param kind The kind of day this service is
     * @param serviceID The ID of this particular service
     * @param serviceNumber The number of this particular service
     * @param route The route this Service operates on.
     * @param serviceDay The day the service is for
     */
    public Service(TimetableInfo.timetableKind kind, 
                    int serviceID, int serviceNumber, 
                    Route route,
                    Calendar serviceDay) 
    {
        this.kind = kind;
        this.serviceID = serviceID;
        this.serviceNumber = serviceNumber;
        this.route = route;
        this.serviceDay = serviceDay;
        this.times = new ArrayList<Calendar>();
        this.busStops = new ArrayList<Location>();
        this.isCancelled = false;

       
        //We now need to generate the times
        int[] serviceTimes = TimetableInfo.getServiceTimes
                (this.route.getRouteID(), kind, this.serviceNumber);


        //for (int i = 0; i < serviceTimes.length; i++)
        //    System.out.print(serviceTimes[i] + ", ");

       // System.out.println();

        //System.out.println("FOR ID: " + serviceID);
        
        if (serviceTimes[0] > serviceTimes[serviceTimes.length - 1])
            this.durationInMinutes = MINUTES_IN_DAY - serviceTimes[0]
                                            + serviceTimes[serviceTimes.length - 1];
        else
            this.durationInMinutes = serviceTimes[serviceTimes.length - 1] - serviceTimes[0];




        //System.out.println("Start: " + serviceTimes[0] + "End: " + serviceTimes[serviceTimes.length - 1] + "Duration: " + durationInMinutes);

        for (int i = 0; i < serviceTimes.length; i++)
        {
            Calendar c = Calendar.getInstance();
            c.set(Calendar.DAY_OF_WEEK, serviceDay.get(Calendar.DAY_OF_WEEK));
            c.set(Calendar.HOUR_OF_DAY, 0);
            c.set(Calendar.MINUTE, serviceTimes[i]);
            c.set(Calendar.SECOND, 0);
            c.set(Calendar.MILLISECOND, 0);
            this.times.add(c);
        }

        //System.out.println("Times length: " + times.size() + " Stops length: " + busStops.size());



        /** The following added by @Yuri*/
        // get all service IDs for the current service
        int[] serviceIDs = database.busDatabase.
                select_ids("timetable_line_id", "timetable_line", "service", serviceID, "timing_point");
        // from these service IDs get all Locations for the current service
        for (int i = 0; i < serviceIDs.length; i++)
        {
            int locationID = database.busDatabase.get_int("timetable_line", serviceIDs[i], "timing_point");
            Location locationToAdd = new Location(locationID);
            this.busStops.add(locationToAdd);
            String locationName = locationToAdd.getStopName();
            if (locationName.equalsIgnoreCase("Back of Beyond"))
               System.out.println("Adding location: Back of Beyond");
        }

        // Set the type of the route by comparing startLocation and endLocation
        if (this.getStartLocation().getStopName().equals(
                                (String) this.getEndLocation().getStopName()))
            this.type = Type.CIRCULAR;
        else
            this.type = Type.STRAIGHT;
    }
    
    
    /**
     * Checks whether a bus is available for the duration of
     * this service.
     * @param bus The bus to check availability for.
     * @return True if the bus is available for the duration of this service,
     *              false otherwise.
     */
    public boolean isAvailableForService(Bus busToCheck)
    {        
        BusShift currentShift = new BusShift(this, busToCheck);
        return busToCheck.getAvailabilityOn(currentShift);
    }
    
    ///////////////////////////
    //GETTERS
    //////////////////////////

    public Route getRoute() {
        return route;
    }

    public int getServiceID() {
        return serviceID;
    }

    public int getServiceNumber() {
        return serviceNumber;
    }

    public ArrayList<Calendar> getTimes() {
        return times;
    }

    public ArrayList<Location> getBusStops()
    {
        return busStops;
    }
    public Calendar getStartTime() {
        return times.get(0);
    }
    
    public Calendar getEndTime() {
        return times.get(times.size() - 1);
    }

    public Location getStartLocation()
    {
        return busStops.get(0);
    }

    public Location getEndLocation()
    {
        return busStops.get(busStops.size() - 1);
    }

    public Type getType()
    {
        return type;
    }
    
    public int getDurationInMinutes() {
        return this.durationInMinutes;
    }

    /**
     * Returns the next stop that this Service will
     * stop at at the given time.
     * @param time The time to use
     * @return The Location this Service will visit next, or null
     *         if no such Location.
     */
    public Location getNextStop(Calendar time)
    {
        System.out.println("--------------");
        System.out.println("Entered Time: " + Service.calendarToDateString(time) + " time: " + Service.calendarToString(time) + "\n");
        for (int i = 1; i < this.times.size(); i++)
        {
            System.out.println(Service.calendarToDateString(times.get(i - 1)) + " time: " + Service.calendarToString(times.get(i)));
            //If the time entered is after a bus stop, and before the next,
            //The Location is the next bus stop
            if ((time.after(this.times.get(i - 1)) || time.equals(this.times.get(i - 1)))
                    && (time.before(this.times.get(i)) || time.equals(this.times.get(i))))
            {
                //Return the next bus stop
                return this.busStops.get(i);
            }
        }

        //Time is either before this service starts, or after
        return null;
    }

    /**
     * Finds the next time the bus will move to a specified stop
     * @param time The current time
     * @return The time this Service will arrive at the stop
     */
    public Calendar getNextTime(Calendar time, String locationName)
    {
        //Can't do anything if cancelled!
        if (isCancelled)
            return null;

        Iterator<Location> locationsIter = this.busStops.iterator();
        Location foundLocation = null;
        int index = 0;
        while (locationsIter.hasNext())
        {
            Location currentLoc = locationsIter.next();
            if (currentLoc.isSameName(locationName))
            {
                foundLocation = currentLoc;
                break;
            }
            
            index++;
        }

        if (foundLocation == null)
            return null;

        Calendar foundTime = this.times.get(index);

        if (foundTime.after(time))
            return foundTime;

        //Return null to indicate failure!
        return null;
    }

    /**
     * Checks whether this Service has the specified Location
     * @param location The location to check
     * @return True if this Service stops at this Location,
     *         false otherwise.
     */
    public boolean hasStop(String location)
    {
        Iterator<Location> stopsIter = this.busStops.iterator();
        while (stopsIter.hasNext())
        {
            if (stopsIter.next().isSameName(location))
                return true;
        }

        return false;
    }


    /**
     * Adds a delay to this service by the specified
     * number of minutes.
     * @param minutesToDelay The number of minutes to delay by
     */
    public void delay(int minutesToDelay, String message)
    {
        //We must iterate through every single time this
        //Service visits a stop and add the minutes to delay
        //to it.
        Iterator<Calendar> timesIter = this.times.iterator();
        while (timesIter.hasNext())
        {
            Calendar currentTime = timesIter.next();
            currentTime.add(Calendar.MINUTE, minutesToDelay);
        }

        this.delayAmount = minutesToDelay;
        this.message = message;
    }

    public void cancel(String message)
    {
        this.isCancelled = true;
        this.message = message;
    }

    public String getExcuseMessage()
    {
        String returnMessage = "Sorry, the " + Integer.toString(this.serviceID) + " is ";
        if (isCancelled)
            returnMessage += "cancelled ";
        else
            returnMessage += "delayed by: " + Integer.toString(this.delayAmount) + " minutes";

        returnMessage += " due to: " + this.message + ".";

        return returnMessage;
    }
    

    // calendar to string
    public static String calendarToString(Calendar cal)
    {
        String returnDate = "" + cal.get(Calendar.HOUR_OF_DAY) + ":";
        if (cal.get(Calendar.MINUTE) < 10)
            returnDate += "0" + cal.get(Calendar.MINUTE);
        else
            returnDate += cal.get(Calendar.MINUTE);
        return returnDate;
    }

    /**
     * Converts a calendar into a full date object
     * @param cal The calendar to convert
     * @return A string representation of a calendar.
     */
    public static String calendarToDateString(Calendar cal)
    {
        return cal.get(Calendar.DATE) 
                + "/" + (cal.get(Calendar.MONTH) + 1)
                + "/" + cal.get(Calendar.YEAR);
    }

    /**
     * Converts a calendar's day field to a string.
     * @param cal The calendar object with a day field
     * @return A string representation of the day field, or null if no day field
     *         can be found.
     */
    public static String dayToString(Calendar cal)
    {
        int day = cal.get(Calendar.DAY_OF_WEEK);

        switch (day)
        {
            case Calendar.MONDAY:
                return "Monday";

            case Calendar.TUESDAY:
                return "Tuesday";

            case Calendar.WEDNESDAY:
                return "Wednesday";

            case Calendar.THURSDAY:
                return "Thursday";

            case Calendar.FRIDAY:
                return "Friday";

            case Calendar.SATURDAY:
                return "Saturday";

            case Calendar.SUNDAY:
                return "Sunday";

            default:
                return null;

        }

    }
    
    @Override
    public String toString() {
        return "Service{" + "serviceID=" + serviceID + ", serviceNumber=" + 
                serviceNumber + ", route=" + route + ", type=" + type + "}\n"
                + "[" + getStartLocation().getStopName() + " @ " + calendarToString(this.getStartTime()) + " - " +
                getEndLocation().getStopName() + " @ " + calendarToString(this.getEndTime()) + "]\n";
    }
    
    
     
    
    /**
     * Get all the services on a particular route.
     * @param route The route to get the services for
     * @param kindOfDay The kind of day the services found should be.
     * @return An ArrayList representing all the services on a route.
     */
    public static ArrayList<Service> getAllServices(Route route, Calendar day,
                                        TimetableInfo.timetableKind kindOfDay)
    {
        int[] allServices = TimetableInfo.getServices(route.getRouteID(), kindOfDay);
        
        //The return ArrayList
        ArrayList<Service> services = new ArrayList<Service>();
        
        for (int i = 0; i < allServices.length; i++)
            services.add(new Service(kindOfDay, allServices[i], i, route, day));
        
        return services;
    }
    
    
    
    
    /**
     * For testing only
     * @param args 
     */
    public static void main(String[] args)
    {
        /*database.openBusDatabase();       
        //Test route 65
        ArrayList<Route> routes = Route.getRoutes();       
        Route route = routes.get(0);
    
        ArrayList<Service> services = getAllServices(route, TimetableInfo.timetableKind.weekday);
        
        Service service = services.get(0);
        System.out.println(service);
        
        ArrayList<Calendar> theTimes = service.getTimes();
        
        Iterator<Calendar> iter = theTimes.iterator();
        while (iter.hasNext())
            System.out.println(iter.next().get(Calendar.MINUTE));*/

        database.openBusDatabase();
                
        TimetableInfo.timetableKind kind = TimetableInfo.timetableKind.weekday;
        //int[] services_r65 = TimetableInfo.getServices(65, kind);
       // int[] services_r66 = TimetableInfo.getServices(66, kind);
        int[] services_r67 = TimetableInfo.getServices(67, kind);
        int[] services_r68 = TimetableInfo.getServices(68, kind);

       //ArrayList<Service> serv65 = getAllServices(new Route(65), Calendar.getInstance(), kind);
        //ArrayList<Service> serv66 = getAllServices(new Route(66), Calendar.getInstance(), kind);
        ArrayList<Service> serv67 = getAllServices(new Route(67), Calendar.getInstance(), kind);
        ArrayList<Service> serv68 = getAllServices(new Route(68), Calendar.getInstance(), kind);
        
       // for (int i = 0; i < serv65.size(); i++)
        //    System.out.println(serv65.get(i));
        //for (int i = 0; i < serv66.size(); i++)
        //    System.out.println(serv66.get(i));
    }
}
